package com.mybatis.jpa.repository;

import com.mybatis.jpa.SqlProvider.*;
import com.mybatis.jpa.meta.MetaCell;
import org.apache.ibatis.annotations.*;
import org.apache.ibatis.binding.MapperProxy;
import org.apache.ibatis.mapping.MappedStatement;
import org.apache.ibatis.session.SqlSession;

import javax.annotation.PostConstruct;
import java.io.Serializable;
import java.lang.reflect.Field;
import java.lang.reflect.Proxy;
import java.util.List;

/**
 * @Author lgt
 * @Date 2017/10/9 : 10:27
 */
public interface Repository<T extends Serializable, K > {

    @SelectProvider(type = SelectSqlProvider.class, method = "selectByPrimaryKey")
    public T selectByPrimaryKey(@Param("pk") K pk);

    @SelectProvider(type = SelectSqlProvider.class, method = "selectAll")
    public List<T> selectAll();

    @SelectProvider(type = SelectSqlProvider.class, method = "countAll")
    public int countAll();

    @SelectProvider(type = SelectSqlProvider.class, method = "selectByCondition")
    public List<T> selectByCondition(Condition condition);

    @SelectProvider(type = SelectSqlProvider.class, method = "countByCondition")
    public int countByCondition(Condition condition);

    @InsertProvider(type = InsertSqlProvider.class, method = "insert")
    @Options(useGeneratedKeys = true ,keyColumn = "id")
    public int insert(T t);

    public default int insertUserGeneratedKey(T t){
        MapperProxy mapperProxy = (MapperProxy)Proxy.getInvocationHandler(this);
        Field sqlSessionField;
        Field mapperField;
        try {
            // get mapper
            mapperField = mapperProxy.getClass().getDeclaredField("mapperInterface");
            mapperField.setAccessible(true);
            Class<?> mapper = (Class<?>) mapperField.get(mapperProxy);
            mapperField.setAccessible(false);
            //get data meta
            MetaCell metaCell = ProviderUtil.getMetaCellByMapper(mapper);
            if (!metaCell.isPrepare()) {
                // make mappedStatement keyProperties and keyColumns
                sqlSessionField = mapperProxy.getClass().getDeclaredField("sqlSession");
                sqlSessionField.setAccessible(true);
                SqlSession sqlSession = (SqlSession)sqlSessionField.get(mapperProxy);
                sqlSessionField.setAccessible(false);
                MappedStatement mappedStatement = sqlSession.getConfiguration().getMappedStatement(mapper.getName() + ".insert");
                String[] keyProperties = mappedStatement.getKeyProperties();
                String[] keyColumns = mappedStatement.getKeyColumns();
                keyColumns[0] = metaCell.getPrimaryKey();
                keyProperties[0] = ProviderUtil.getKeyProperty(mapper);
                metaCell.setPrepare(true);
            }
        } catch (Exception e) {
            throw new RuntimeException("fail make mappedStatement keyProperties and keyColumns ",e);
        }
        return insert(t);
    }

    @InsertProvider(type = InsertSqlProvider.class, method = "insertBatch")
    public int insertBatch(@Param("list") List<T> list);

    @DeleteProvider(type = DeleteSqlProvider.class, method = "deleteByPrimaryKey")
    public int deleteByPrimaryKey(@Param("pk") K pk);

    @DeleteProvider(type = DeleteSqlProvider.class,method = "deleteByCondition")
    public int deleteByCondition(Condition condition);

    @UpdateProvider(type = UpdateSqlProvider.class, method = "updateByPrimaryKey")
    public int updateByPrimaryKey(@Param("pk") K pk,@Param("t") T t);

    @UpdateProvider(type = UpdateSqlProvider.class, method = "updateByPrimaryKeySelectivity")
    public int updateByPrimaryKeySelectivity(@Param("pk") K pk,@Param("t") T t);

    @UpdateProvider(type = UpdateSqlProvider.class, method = "updateByConditionSelectivity")
    public int updateByConditionSelectivity(Condition condition);

}
